import VNode from "./vnode.js";
/**
 * 在 Vue 实例上安装运行时的渲染帮助函数，比如 _c、_v，这些函数会生成 Vnode
 * @param {VueContructor} target Vue 实例
 */
export default function renderHelper(target) {
  /**
   * @param {vm} target vm实例
   * 将 ._c 和 ._v 绑定到实例上去
   * 1._c 创建VNode标签
   * 2. _v 创建文本节点
   */
  target._c = createElement;
  target._v = createTextNode;
  target._t = renderSlots;
}
/**
 * 根据标签信息创建 Vnode
 * @param {string} tag 标签名
 * @param {Map} attr 标签的属性 Map 对象
 * @param {Array<Render>} children 所有的子节点的渲染函数
 */
function createElement(tag, attr, children) {
  return VNode(tag, attr, children, this);
}

/**
 * 生成文本节点的 VNode
 * @param {*} textAst 文本节点的 AST 对象
 */
function createTextNode(textAst) {
  return VNode(null, null, null, this, textAst);
}
/**
 * 生成插槽节点
 * @param {*} textAst
 * 插槽本质: 生成VNode
 * 复杂点: 生成VNode之间的各种解析, 也就是数据准备阶段
 */
function renderSlots(attrs, children) {
  // 获取父元素得属性
  // console.log("this._parentVnode ==> ", this._parentVnode);
  const parentAttr = this._parentVnode.attr;
  let vnode;
  if (parentAttr.scopedSlots) {
    // 说明当前组件得插槽传递了内容
    // 获取插槽信息
    const slotName = attrs.name;
    const slotInfo = parentAttr.scopedSlots[slotName];
    // 设置作用于绑定的变量 attrs.vBind  {slotKey: "slotKey"}
    // Object.keys(attrs.vBind)[0] 获取对象中的第一个元素
    // slotInfo.scopedSlot 是 v-bind:slotKey="slotKey" 得 value  slotKey， slotKey是动态绑定的数据
    // 绑定到data上
    this[slotInfo.scopedSlot] = this[Object.keys(attrs.vBind)[0]];
    // 生成vnode
    vnode = genVnode(slotInfo.children, this);
  } else {
    // 没传递 显示默认内容 也就是children
    vnode = genVnode(children, this);
  }
  if (children.length === 1) {
    // 表示只有一个内容
    return vnode[0];
  }
  // 有多个
  return createElement.call(this, "div", {}, vnode);
}

/**
 * 将一批 ast 节点数组 转换成 vnode 数组
 */
function genVnode(childs, vm) {
  const vnode = [];
  for (let i = 0; i < childs.length; i++) {
    const { tag, attrs, children, text } = childs[i];
    if (text) {
      if (typeof text === "string") {
        // 文本节点
        const textAst = {
          type: 3,
          text,
        };
        // 是否使用了表达式
        if (text.match(/{{(.*?)}}/)) {
          // 说明是表达式
          textAst.expression = RegExp.$1.trim();
        }
        vnode.push(createTextNode.call(vm, textAst));
      } else {
        // text 为文本节点得AST对象
        vnode.push(createTextNode.call(vm, text));
      }
    } else {
      // 元素节点 this 指向vm
      vnode.push(createElement.call(vm, tag, attrs, genVnode(children, vm)));
    }
  }

  return vnode;
}
